﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Diagnostics;

namespace AdobePolicyFileServer
{
    class TCPServer :IDisposable
    {
        public static IPAddress DEFAULT_IP = IPAddress.Parse("0.0.0.0");
        public static int DEFAULT_PORT=843;
        public static IPEndPoint DEFAULT_IP_END_POINT = new IPEndPoint(DEFAULT_IP, DEFAULT_PORT);

        /// <summary>
        /// Local Variables Declaration.
        /// </summary>
        private TcpListener listener = null;
        private bool isStopped=false;
        private bool isCleaned=false;
        private Thread mainThread = null;
        private Thread cleaningThread = null;
        private List<SocketListener> socketListeners = null;

        private EventLog logger;

        public EventLog Logger
        {
            get { return logger; }
            set { 
                logger = value; 
            }
        }

        public TCPServer()
        {
            Init(DEFAULT_IP_END_POINT);
        }
        public TCPServer(IPAddress serverIP)
        {
            Init(new IPEndPoint(serverIP, DEFAULT_PORT));
        }

        public TCPServer(int port)
        {
            Init(new IPEndPoint(DEFAULT_IP, port));
        }

        public TCPServer(IPAddress serverIP, int port)
        {
            Init(new IPEndPoint(serverIP, port));
        }

        public TCPServer(IPEndPoint ipNport)
        {
            Init(ipNport);
        }

        private void Init(IPEndPoint ipNport)
        {
            try
            {
                
                listener = new TcpListener(ipNport);       
            }
            catch 
            {
                listener=null;
            }
        }

        public void Start()
        {
            if (logger != null)
                logger.WriteEntry("Server starts. Bind to " + this.listener.Server.AddressFamily );
            if (listener!=null)
            {
                // Create a ArrayList for storing SocketListeners before
                // starting the server.
                socketListeners = new List<SocketListener>(100);

                // Start the Server and start the thread to listen client 
                // requests.
                listener.Start();
                mainThread = new Thread(new ThreadStart(ListenerThreadStart));
                mainThread.Priority = ThreadPriority.Normal;
                mainThread.Start();

                // Create a low priority thread that checks and deletes client
                // SocktConnection objcts that are marked for deletion.
                cleaningThread = new Thread(new ThreadStart(CleaningThreadStart));
                cleaningThread.Priority=ThreadPriority.Lowest;
                cleaningThread.Start();
                isStopped = false;
            }
        }

        /// <summary>
        /// TCP/IP Server Thread that is listening for clients.
        /// </summary>
        private void ListenerThreadStart()
        {
            // Client Socket variable;
            Socket clientSocket = null;
            SocketListener socketListener = null;
            while (!isStopped)
            {
                try
                {
                    //if (listener.Pending())
                    //{
                        // Wait for any client requests and if there is any 
                        // request from any client accept it (Wait indefinitely).
                        clientSocket = listener.AcceptSocket();

                        // Create a SocketListener object for the client.
                        socketListener = new SocketListener(clientSocket,this.logger);

                        lock (socketListeners)
                        {
                            socketListeners.Add(socketListener);
                        }

                        socketListener.Start();
                    //}
                }
                catch (SocketException se)
                {
                    if (logger != null)
                        logger.WriteEntry(se.Message);
                    isStopped = true;
                }
            }
        }


        private void CleaningThreadStart()
        {
            while (!isCleaned)
            {
                List<SocketListener> deleteList = new List<SocketListener>(10);
                lock (socketListeners)
                {
                    foreach (SocketListener socketListener in socketListeners)
                    {
                        if (socketListener.IsForDeletion)
                        {
                            deleteList.Add(socketListener);
                            socketListener.Stop();
                        }
                    }

                    foreach (SocketListener socket in deleteList)
                    {
                        socketListeners.Remove(socket);
                    }
                }
                Thread.Sleep(10000);
            }
        }

        private void StopSocketListeners()
        {
            
            foreach (SocketListener socket in socketListeners)  
            {
                socket.Stop();
            }
            lock (socketListeners)
            {
                socketListeners.Clear();
            }
            
        }

        #region IDisposable Members

        public void Dispose()
        {
            Stop();
        }

        #endregion

        internal void Stop()
        {
            if (listener!=null)
            {

                // Stop the TCP/IP Server.
                isStopped=true;
                listener.Stop();

                // Wait for one second for the the thread to stop.
                mainThread.Join(1000);

                // If still alive; Get rid of the thread.
                if (mainThread.IsAlive)
                {
                    mainThread.Abort();
                }
                mainThread=null;

                isCleaned=true;
                cleaningThread.Join(1000);
                if (cleaningThread.IsAlive)
                {
                    cleaningThread.Abort();
                }
                cleaningThread=null;

                // Free Server Object.
                listener = null;

                // Stop All clients.
                StopSocketListeners();

                if(logger != null)
                    logger.WriteEntry("Server stopped");
            }
        }
    }
}
